﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Threading;
using chromium.Log;
using chromium.Utils;
using System.Threading.Tasks;

namespace chromium.GAE
{
    public class DnsHelper
    {
        private static readonly ILog logger = LogManager.GetLogger(typeof(DnsHelper));
        private static readonly List<DnsItem> googleHost = new List<DnsItem>();
        private static Timer refreshTimer;

        public static void Start()
        {
            foreach (string ip in Settings.IP)
            {
                IPAddress ipAddress;
                if (!IPAddress.TryParse(ip, out ipAddress))
                {
                    IPAddress[] ipAddresses = Dns.GetHostAddresses(ip);
                    googleHost.AddRange(ipAddresses.Select(address => new DnsItem(address, Settings.Https)));
                }
                else
                {
                    DnsItem item = new DnsItem(ipAddress, Settings.Https);
                    googleHost.Add(item);
                }
            }

            TestAccessbility(null);
            //refreshTimer = new Timer(TestAccessbility, null, 0, 5 * 60 * 1000);
        }

        private static void TestAccessbility(object state)
        {
            Task.Factory.ContinueWhenAll(googleHost.Select(item => Task.Factory.StartNew(TestAccessbilityAction, item)).ToArray(), Sort);
        }

        private static void Sort(object state)
        {
            googleHost.RemoveAll(item => item.Delay == -1);

            googleHost.Sort((a, b) => a.Delay.CompareTo(b.Delay));

            //SaveIpConfig();

            logger.InfoFormat("Google Host sorted, Fast={0} {1}ms", googleHost[0].Address, googleHost[0].Delay);
        }

        private static void SaveIpConfig()
        {
            Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            config.AppSettings.Settings.Remove("IP");
            config.AppSettings.Settings.Add("IP", googleHost.Aggregate(string.Empty, (current, dnsItem) => current + (dnsItem.Address.ToString() + "|")));
            config.Save(ConfigurationSaveMode.Modified);
        }

        private static void TestAccessbilityAction(object parameter)
        {
            HttpWebResponse response = null;
            HttpWebRequest requestToProxy = null;
            DnsItem dnsItem = (DnsItem)parameter;
            try
            {
                string url = string.Format("{0}://{1}:{2}/{3}", dnsItem.Https ? "https" : "http", dnsItem.Address, Settings.SpdyPort, Settings.Mode);
                requestToProxy = (HttpWebRequest)WebRequest.Create(url);
                requestToProxy.Host = Settings.App[0];
                requestToProxy.Proxy = EmptyProxy.Instance;
                requestToProxy.Timeout = 5000;
                requestToProxy.AllowAutoRedirect = false;
                requestToProxy.KeepAlive = false;

                Stopwatch stopWatch = new Stopwatch();
                stopWatch.Start();

                response = (HttpWebResponse)requestToProxy.GetResponse();

                stopWatch.Stop();

                if (response.StatusCode == HttpStatusCode.OK)
                {
                    dnsItem.Delay = stopWatch.ElapsedMilliseconds;
                    logger.InfoFormat("{0} - OK, https = {1}", dnsItem.Address, dnsItem.Https);
                }
                else
                {
                    dnsItem.Delay = -1;
                    logger.InfoFormat("{0} - unavailable, https={1} {2}", dnsItem.Address, dnsItem.Https, response.StatusCode);
                }
            }
            catch (Exception e)
            {
                dnsItem.Delay = -1;
                logger.InfoFormat("{0} - unavailable, https={1} {2}", dnsItem.Address, dnsItem.Https, e.Message);
            }
            finally
            {
                if (response != null)
                {
                    response.Close();
                }
                if (requestToProxy != null)
                {
                    requestToProxy.Abort();
                }
            }
        }

        public static string GetHost(string host)
        {
            int index = Math.Abs(host.GetHashCode() % Settings.App.Count);
            return googleHost[Math.Min(index, googleHost.Count - 1)].Address.ToString();
        }

        public static string GetGaeHost(string url)
        {
            return Settings.App[Math.Abs(url.GetHashCode() % Settings.App.Count)];
        }
    }
}
